/**
*cache driver
*TODO 搜索整个组， 确定是否有效
*TODO 读命中的活动
*TODO 读缺失的活动
*/
`timescale 1ns / 1ps
module cache_top (//
    input           clk,
    input           resetn,
    //Sram-Like接口信号定义
    input           cpu_req,//cpu 读信号
    input[31:0]     cache_addr,// cpu 给出的cache地址
    output[31:0]    cache_rdata,// cpu 读出数据
    output          cache_addr_ok,// 若命中， 则为1
    output          cache_data_ok,// 数据准备完毕， 则为1
    //AXI接口信号定义 暂时可以不用管下列接口（2022年11月22日）
    output[3:0]     arid,
    output          arvalid,
    input           arready,

    input[3:0]      rid,
    input[31:0]     rdata,
    input           rlast,
    input           rvalid,
    output          rready    
);
wire rst=~resetn;
/**DFA
*IDLE:空转->random stat
*RUN:运行状态
*SEL_WAY:未命中
*MISS:这个状态时，cache会更新LRU, 并发起AXI请求， 从存储器中读取未命中的行， 如果允许读，就转向REFILE
*FINISH:数据传输完成，Cache在下一个状态回到IDLE状态
*RESETN:重置DFA，持续128个周期，直到专用计数器为127时回到IDLE状态
*/
localparam IDLE = 4'b0000,
            RUN = 4'b0001,
            WAIT = 4'b0010,
            SEL_WAY = 4'b0011,
            MISS = 4'b0100,
            FINISH = 4'b0101,
            REFILL = 4'b0110,
            RESETN = 4'b0111,
            CACHE_GROUP_NUM=2047,
            WAIT_LATENCY=4'ha;
reg[3:0]    cur_stat;
reg[3:0]    next_stat;
reg[6:0]    rst_latency;
// reg[1:0]    LRU[0:2047][0:1];
// reg[1:0]    LRU[0:2047];
// cache_line[161:0]   [valid(1) |tag(16) |block(64)|valid(1)|tag(16)|block(64)]
// cpu_addr     [tag(16) |group idx(11)|block offset(5)]

wire[323:0]  cache_line;
    
wire[10:0]  idx_from_cpu=cache_addr[15:5];// 组号

wire[15:0]  tag_from_cpu=cache_addr[31:16];// cpu给出的tag

wire[4:0]   ofs_from_cpu=cache_addr[4:0];// 块内偏移

wire        valid1_from_cache=cache_line[323:323];
wire        valid2_from_cache=cache_line[242:242];
wire        valid3_from_cache=cache_line[161:161];
wire        valid4_from_cache=cache_line[80:80];// 有效位

// tag
wire[15:0]        tag1_from_cache=cache_line[322:307];
wire[15:0]        tag2_from_cache=cache_line[241:226]; 
wire[15:0]        tag3_from_cache=cache_line[160:145];
wire[15:0]        tag4_from_cache=cache_line[79:64];// cache读出的tag
reg[32:0] wait_cnt=0;
wire wait_end=(wait_cnt>=1);
reg cache_rreq=0;
wire hit  =((tag1_from_cache==tag_from_cpu)||(tag2_from_cache==tag_from_cpu)||(tag3_from_cache==tag_from_cpu)||(tag4_from_cache==tag_from_cpu)) 
                && (cur_stat==RUN&&wait_end) 
                && (valid1_from_cache||valid2_from_cache||valid3_from_cache||valid4_from_cache);
wire[1:0] target=(tag1_from_cache==tag_from_cpu)?2'b0:((tag2_from_cache==tag_from_cpu)?2'b1:(tag3_from_cache==tag_from_cpu)?2'b10:2'b11);
wire miss = !hit&&(cur_stat==RUN&&wait_end);

assign cache_data_ok = (cur_stat==FINISH);
assign cache_addr_ok = (cpu_req&&cur_stat==RUN&&next_stat!=RUN);

reg[323:0]      data_to_mem=323'b0;
reg             wea=0;
reg[31:0]       reg4b=0;
wire[63:0]       data_from_ram;
assign cache_rdata=hit?((tag1_from_cache==tag_from_cpu)?cache_line[144-ofs_from_cpu-:32]:cache_line[63-ofs_from_cpu-:32]):data_from_ram[63-ofs_from_cpu-:32];



reg axiOver;

reg[6:0]        initCnt=0;
wire initEnd=(initCnt==31);
// 复位
always @(posedge clk) begin
        if(cur_stat==RESETN) initCnt<=initCnt+1;
        else if(initEnd) initCnt<=0;
        else initCnt<=initCnt;
end
reg[3:0]  lru_param[3:0];
generate
        genvar indx;
        for(indx=0;indx<4;indx=indx+1)begin:lru_param_gen
        always@(posedge clk)begin
        case(indx)
        0:lru_param[indx]<=4'b1110;
        1:lru_param[indx]<=4'b1101;
        2:lru_param[indx]<=4'b1011;
        3:lru_param[indx]<=4'b0111;
        default:lru_param[indx]<=4'b0111;
        endcase
        end
        end
endgenerate
reg[3:0] LRU_UNIT[2047:0][3:0];
reg[1:0] free_way;
generate
        genvar i,j;
        for(i=0;i<2048;i=i+1)begin: lru_generator_row
                for(j=0;j<4;j=j+1)begin:lru_generator_col
                always @(posedge clk) begin
                if(!resetn)begin
                           LRU_UNIT[i][j]<=4'h0;     
                        end
                else if(cur_stat==MISS)begin
                        if(j==free_way&&i==idx_from_cpu)
                                LRU_UNIT[idx_from_cpu][free_way]<=lru_param[free_way];
                        else
                                LRU_UNIT[idx_from_cpu][j][free_way]<=1'b0;
                end else if(cur_stat==RUN&&next_stat==FINISH)begin
                        if(j==target&&i==idx_from_cpu)
                                LRU_UNIT[idx_from_cpu][target]<=4'h7;
                        else if(i==idx_from_cpu)begin
                                LRU_UNIT[idx_from_cpu][j][target]<=1'b0;

                        end
                        else   
                                LRU_UNIT[i][j]<=LRU_UNIT[i][j];
                end else begin
                        LRU_UNIT[i][j]<=LRU_UNIT[i][j];
                end  
                end       
                end    
        end
endgenerate
wire[3:0] lru_monitor=LRU_UNIT[idx_from_cpu][0];
// DFA
always @(posedge clk or negedge resetn) begin
    if(!resetn) cur_stat<=RESETN;
    else        cur_stat<=next_stat;
end
always @(posedge clk) begin
    if(!resetn) wait_cnt<=0;
    else if(wait_end) wait_cnt<=0;
    else if(cur_stat==RUN&&cpu_req) wait_cnt<=wait_cnt+1;
    else wait_cnt<=0;
end
always@(*)begin
    case (cur_stat)
        IDLE:   begin 
                next_stat=RUN;
                cache_rreq=0;   
                end
        RUN:    if(hit) begin
                next_stat=FINISH;
                cache_rreq=0;
                end
                else if(cpu_req&&miss&&wait_end) begin // miss
                        next_stat=SEL_WAY;
                        cache_rreq=0;
                end
                else begin 
                        next_stat=RUN;
                        cache_rreq=0;
                end

        SEL_WAY:begin 
                next_stat=MISS;
                cache_rreq=0;
                end

        MISS:   
                begin
                next_stat=REFILL;
                cache_rreq=1;
                end

        REFILL: begin 
                next_stat=FINISH;
                cache_rreq=1;
                end
        RESETN: if(initEnd) begin 
                next_stat=IDLE;
                cache_rreq=0;
                end
                else begin 
                next_stat=RESETN;
                cache_rreq=0;
                end
	FINISH:	begin
                next_stat=IDLE;
                cache_rreq=0;
                end
    endcase
end
// 读命中，读时序

// 给存储器的地址， 理论上只有命中了之后才会执行，


cache_mem mem(
    .clka   (clk            ),
    .addra  (idx_from_cpu   ),
    .dina   (data_to_mem    ),
    .wea    (wea            ),
    .douta  (cache_line     )
);

/********************************
*这里处理缺失的相关处理
*********************************/
always @(negedge resetn or posedge clk) begin
        if(!resetn)begin
                free_way<=0;
        end else if(cur_stat==SEL_WAY)begin
                free_way<=(LRU_UNIT[idx_from_cpu][0]==4'b0)?2'b0:
                        ((LRU_UNIT[idx_from_cpu][1]==4'b0)?2'b1:
                                ((LRU_UNIT[idx_from_cpu][2]==4'b0)?2'b10:2'b11));
        end else if(cur_stat==RUN&&next_stat==FINISH)begin
                free_way<=0;
        end else begin 
                free_way<=free_way;
        end
end

/**
*将从ram中读取的数据写入mem
*/
always @(posedge clk) begin
        if(cur_stat==MISS&&next_stat==REFILL)begin
                wea<=1;
                case (free_way)
                        2'b1: data_to_mem<={cache_line[323:243], 1'b1,tag_from_cpu[15:0],data_from_ram[63:0],cache_line[161:0]};
                        2'b10:data_to_mem<={cache_line[323:162], 1'b1,tag_from_cpu[15:0],data_from_ram[63:0], cache_line[80:0]};
                        2'b11:data_to_mem<={cache_line[323:81],1'b1,tag_from_cpu[15:0],data_from_ram[63:0]};
                        default: data_to_mem<={1'b1,tag_from_cpu[15:0],data_from_ram[63:0],cache_line[242:0]};
                endcase
        end
        else begin
                wea<=0;
                data_to_mem<=0;
        end
end
/*
*REFILL TODO
*/
/**
牺牲cache设计
*/

main_mem1 ram(
        .clka   (clk          ),
        .addra  (tag_from_cpu  ),
        // .ena    (cache_rreq  ),
        .douta  (data_from_ram)
);
endmodule